package view

import (
	"errors"
)

const (
	CONSTANT_UTF_8                = 1
	CONSTANT_INTEGER              = 3
	CONSTANT_FLOAT                = 4
	CONSTANT_LONG                 = 5
	CONSTANT_DOUBLE               = 6
	CONSTANT_CLASS                = 7
	CONSTANT_STRING               = 8
	CONSTANT_FIELD_REF            = 9
	CONSTANT_METHOD_REF           = 10
	CONSTANT_INTERFACE_METHOD_REF = 11
	CONSTANT_NAME_AND_TYPE        = 12
	CONSTANT_METHOD_HANDLE        = 15
	CONSTANT_METHOD_TYPE          = 16
	CONSTANT_INVOKE_DYNAMIC       = 18
)

type ConstantPoolInfo struct {
	ConstantList []Constant
}

type Constant interface {
	ReadConstant(cr *ClassReader)
	ReadValue(cr ClassReader, cp ConstantPoolInfo)
}

func ReadConstantInfo(cr *ClassReader) (Constant, error) {

	tag := cr.readUInt8()
	c, err := NewConstantInfo(tag)
	if err != nil {
		return nil, err
	}
	c.ReadConstant(cr)
	return c, nil
}

func NewConstantInfo(tag uint8) (Constant, error) {
	switch tag {
	case CONSTANT_INTEGER:
		return &Constant_Integer{Tag: tag}, nil

	case CONSTANT_FLOAT:
		return &Constant_Float{Tag: tag}, nil

	case CONSTANT_LONG:
		return &Constant_Long{Tag: tag}, nil

	case CONSTANT_DOUBLE:
		return &Constant_Double{Tag: tag}, nil

	case CONSTANT_UTF_8:
		return &Constant_Utf8{Tag: tag}, nil

	case CONSTANT_STRING:
		return &Constant_String{Tag: tag}, nil

	case CONSTANT_CLASS:
		return &Constant_Class{Tag: tag}, nil

	case CONSTANT_NAME_AND_TYPE:
		return &Constant_Name_And_Type{Tag: tag}, nil

	case CONSTANT_FIELD_REF:
		return &Constant_Field_Ref{Tag: tag}, nil

	case CONSTANT_METHOD_REF:
		return &Constant_Method_Ref{Tag: tag}, nil

	case CONSTANT_INTERFACE_METHOD_REF:
		return &Constant_Interface_Method_Ref{Tag: tag}, nil

	case CONSTANT_METHOD_TYPE:
		return &Constant_Method_Type{Tag: tag}, nil

	case CONSTANT_METHOD_HANDLE:
		return &Constant_Method_Handle{Tag: tag}, nil

	case CONSTANT_INVOKE_DYNAMIC:
		return &Constant_InvokeDynamic{Tag: tag}, nil
	default:
		return nil, errors.New("invalid tag in constant pool")
	}
}

func ReadConstantPool(cr *ClassReader) ConstantPoolInfo {
	size := int(cr.readUInt16(cr.readUInt8s(2)))
	cs := make([]Constant, size-1)
	cp := &ConstantPoolInfo{ConstantList: cs}
	for i := 0; i < size-1; i++ {
		c, err := ReadConstantInfo(cr)
		if err == nil {
			cs[i] = c
		}
		_, okL := c.(*Constant_Long)
		if okL {
			i += 1
		} else {
			_, okD := c.(*Constant_Double)
			if okD {
				i += 1
			}

		}
	}
	for i := 0; i < len(cs); i++ {
		c := cs[i]
		if c != nil {
			c.ReadValue(*cr, *cp)
		}
	}
	return *cp
}
